package algo_annotations.data.annotations;

import algo_annotations.data.sites.GroupeSite;
import algo_annotations.data.sites.Site;
import algo_annotations.data.sites.SousGroupeSite;
import algo_annotations.heuristique.Genome;
import algo_annotations.params.Params_a;
import commons.utils.UtilsGeom;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;

public class GroupeAnno {

	//L'ordre des Anno est tres important et doit etre conservé durant tout le traitement
	// et lors des copies de groupe d'anno (il doit correspondre à l'ordre des Site correspondant)
	// car leur place dans vAnno les identifie dans les structures d'optimisation du Vivier
	public ArrayList<Anno> vAnno;
	int nbAnno;
	Params_a params;

	// on stocke les notes dans un tableau pour en extraire le dernier decile
	//  qui sert pour la note globale
	private double[] tabNotes;

	public GroupeAnno(GroupeSite grpSite, Params_a params) {
		this.params = params;
		nbAnno = grpSite.nbSite();
		vAnno = new ArrayList<Anno>(nbAnno);

		for (int i=0 ; i< grpSite.nbSite() ; i++) {
			Anno a = new Anno (grpSite.vSite.get(i));
			a.setPosition (0.5f, 0.5f, -1);
			vAnno.add(a);
		}

		tabNotes = new double[nbAnno];
	}

	
	public void becomeCopyOf(GroupeAnno ga) {
		assert this.nbAnno == ga.nbAnno;
		for (int i=0; i<nbAnno; i++) {
			Anno anno_this = this.vAnno.get(i);
			Anno anno_autre = ga.vAnno.get(i);
			anno_this.setPosition(anno_autre.geneX, anno_autre.geneY, anno_autre.geneML);
		}
	}


	private boolean annoIntersectRectMax(Anno anno, ArrayList<Site> vSiteProche) {
		for (Site siteProche : vSiteProche) {
			if (anno.site==siteProche) continue;
			if (UtilsGeom.areRectIntersected(anno.rect_protection_superpositionAnno, siteProche.rectMax_reel_m))
				return true;
		}
		return false;
	}


    // Calcule les notes sur l'ensemble des anno du groupe
	public double computeNote() {
		return computeNote_forGenome(null, null);
	}

    // Calcule les notes sur l'ensemble des anno du groupe correspondant au sous groupe site donné
	// pour toutes les anno si sous ssGrpSite==null
    // stocke les notes individuelles des anno et la globale dans le genome (si non null) et renvoie la note moyenne
    public double computeNote_forGenome(SousGroupeSite ssGrpSite, Genome genome) {

		if (ssGrpSite !=null && genome != null)
			updateFromGenome(ssGrpSite, genome);

		precomputeAbiguiteSimple_passe1(ssGrpSite, genome);

		double noteMoyenne = 0;
		double noteMax = 0;
		double noteGlobale;


		int nbAnnoANoter;
		if (ssGrpSite != null)
			nbAnnoANoter = ssGrpSite.size();
		else
			nbAnnoANoter = nbAnno;

		// Variable utilisée par toutes les anno, va soulager les appels multiples a certaines
		//  fonctions de UtilsGeom, qui creent bcp de double[4] ce qui sature le GC en cas de
		//  nombreux appels (ex. gds viviers)
		double[] tempData = new double[4];

		int nbAnnoReellementNotee = 0;
		for (int i=0 ; i<nbAnnoANoter ; i++) {
			Anno a;
			
			if (ssGrpSite != null)
				a = vAnno.get(ssGrpSite.vIndexSite.get(i));
			else
				a = vAnno.get(i);

			if (!a.site.hasAnno()) {
				// Site sans libelle
				if (genome != null)	genome.setNote(i, -1);

			} else {
				// Site normal
				double noteAnno = a.computeNote_normal(this, tempData);
				if (genome != null)	genome.setNote(i, noteAnno);

				noteMoyenne += noteAnno;
				tabNotes[nbAnnoReellementNotee] = noteAnno;
				nbAnnoReellementNotee++;
			}

		}
		noteMoyenne /= nbAnnoReellementNotee;
		noteMax = getMoyenneDernierDecile(tabNotes, nbAnnoReellementNotee);

//		noteGlobale = (0.7 * noteMoyenne + 0.3 * noteMax); // cette notation induit des retours en arriere lors de la 2eme passe
		noteGlobale = noteMoyenne;

		if (genome != null)
			genome.setNoteGlobale(noteGlobale);

		return noteGlobale;
    }


	// Calcul pour toutes les anno si sous ssGrpSite==null
	public void precomputeAbiguiteSimple_passe1(SousGroupeSite ssGrpSite, Genome genome) {
		int nbAnnoANoter;
		if (ssGrpSite != null)
			nbAnnoANoter = ssGrpSite.size();
		else
			nbAnnoANoter = nbAnno;

		for (int i=0 ; i<nbAnnoANoter ; i++) {
			Anno a;
			if (ssGrpSite != null)
				a = vAnno.get(ssGrpSite.vIndexSite.get(i));
			else
				a = vAnno.get(i);

			if (!a.site.hasAnno())
				continue;

			// Site normal
			a.precomputeAmbiguiteSimple_passe1(this);
		}
	}



	// attention, tab n'est pas forcément rempli complètement, d'ou la prise en compte de la variable nbValue
	private double getMoyenneDernierDecile(double[] tab, int nbValue) {
		if (nbValue<=9) return 0;

		Arrays.sort(tab, 0, nbValue);
		double moy=0;
		int nbValuesDecile=0;
		for (int i=nbValue-1; i>nbValue*0.9 ; i--) {
			if (tab[i]==-1) break; // on arrive à des anno sans libelle ...
			moy += tab[i];
			nbValuesDecile++;
		}
		if (nbValuesDecile>0)
			return moy / nbValuesDecile;
		else
			return 0;
	}

	// met a jour les position des annos correspondant au sousGrpSite
	public void updateFromGenome(SousGroupeSite ssGrpSite, Genome genome) {
		assert (genome.getTaille() == ssGrpSite.size());

		int nbAnnoSsGrpSite = ssGrpSite.size();

		if (params.bUseMultiligne)
			for (int i=0; i<nbAnnoSsGrpSite ; i++) {
				// le sous groupe de site contient les index des annos concernées par le sous groupe
				Anno a = vAnno.get(ssGrpSite.vIndexSite.get(i));
				a.setPosition(genome.getDX(i), genome.getDY(i), genome.getML(i));
			}
		else
			for (int i=0; i<nbAnnoSsGrpSite ; i++) {
				// le sous groupe de site contient les index des annos concernées par le sous groupe
				Anno a = vAnno.get(ssGrpSite.vIndexSite.get(i));
				a.setPosition(genome.getDX(i), genome.getDY(i), -1);
			}
	}


	// Renvoie null si rien trouvé
	public Anno getPireAnno(double seuilNote, HashSet<Site> setSiteNotToTake) {
		double pireNote = seuilNote;
		Anno pireAnno = null;
		for (Anno anno : vAnno) {
			if (!anno.site.hasAnno()) continue;
			if (anno.noteGlobale > pireNote && !setSiteNotToTake.contains(anno.site)) {
				pireNote = anno.noteGlobale;
				pireAnno = anno;
			}
		}
		return pireAnno;
	}



}
